﻿//===============================================================================
// Microsoft patterns & practices Enterprise Library Contribution
// Query Application Block
//===============================================================================

using System;
using System.Data;
using System.Collections.Generic;
using Microsoft.Practices.EnterpriseLibrary.Common.Configuration;
using Microsoft.Practices.EnterpriseLibrary.Data;
using EntLibContrib.Query.Instrumentation;
using EntLibContrib.Query.Configuration;
using EntLibContrib.Query.Commands;
using EntLibContrib.Query.Properties;

namespace EntLibContrib.Query.Queries
{
	/// <summary>
	/// Data Query.
	/// </summary>
	/// <remarks>
	/// This provider creates a query for a database.
	/// </remarks>
	[ConfigurationElementType(typeof(DataQueryData))]
	public class DataQuery : QueryBase
	{
		#region Constants
		private QueryType queryType = QueryType.Data;
		#endregion

		#region Fields
		private Database database;
		private string command;
		private CommandType commandType;
		#endregion

		#region Properties
		/// <summary>
		/// Gets the type of the query.
		/// </summary>
		/// <value>The type of the query.</value>
		public override QueryType QueryType
		{
			get { return queryType; }
		}

		/// <summary>
		/// Gets or sets the command.
		/// </summary>
		/// <value>The command.</value>
		public string Command
		{
			get { return command; }
			set { command = value; }
		}

		/// <summary>
		/// Gets or sets the type of the command.
		/// </summary>
		/// <value>The type of the command.</value>
		public CommandType CommandType
		{
			get { return commandType; }
			set { commandType = value; }
		}

		#endregion

		#region Construction
		/// <summary>
		/// Initializes a new default instance of the <see cref="DataQuery"/> class.
		/// </summary>
		/// <param name="name">The query name.</param>
		/// <param name="database">The database.</param>
		/// <param name="command">The command.</param>
		/// <param name="commandType">Type of the command.</param>
		/// <param name="customCommand">The custom command.</param>
		/// <param name="parameterSet">The parameter set.</param>
		/// <param name="instrumentationProvider">The instrumentation provider.</param>
		public DataQuery(string name,
										Database database,
										string command,
										CommandType commandType,
										ICustomCommand customCommand,
										IParameterSet parameterSet,
										QueryInstrumentationProvider instrumentationProvider)
			: base(name, customCommand, parameterSet, instrumentationProvider)
		{
			ValidateAndStoreParameters(database, command, commandType);
		}
		#endregion

		#region Public Methods
		/// <summary>
		/// Executes a read command and returns the results.
		/// </summary>
		/// <param name="parameterValues">The parameter values.</param>
		/// <returns>
		/// Dataset containing the results of the read operation
		/// </returns>
		/// <remarks>
		/// 	<para>This method takes the parameter values from the given dictionary.</para>
		/// 	<para>Output parameters will update the parameter property values.</para>
		/// </remarks>
		public override DataSet ExecuteForRead(IDictionary<string, object> parameterValues)
		{
			try
			{
				// set the parameter values given in the dictionary
				if (parameterValues != null)
					SetParameterValues(parameterValues);

				// invoke the service method which must return a DataSet
				DataSet results = BuildCommand().ExecuteForRead();

				// update the instrumentation counters
				UpdateReadCounters(results);

				// return the results
				return results;
			}
			catch (Exception exception)
			{
				// log and rethrow the error
				LogReadError(exception);
				throw;
			}
		}

		/// <summary>
		/// Executes a write command and saves data to the data source.
		/// </summary>
		/// <param name="parameterValues">The parameter values.</param>
		/// <remarks>
		/// 	<para>This method takes the parameter values from the given dictionary.</para>
		/// 	<para>Output parameters will update the parameter property values.</para>
		/// </remarks>
		public override void ExecuteForWrite(IDictionary<string, object> parameterValues)
		{
			try
			{
				// set the parameter values given in the dictionary
				if (parameterValues != null)
					SetParameterValues(parameterValues);

				// invoke the service method which must return void
				BuildCommand().ExecuteForWrite();

				// update the instrumentation counters
				UpdateWriteCounters();
			}
			catch (Exception exception)
			{
				// log and rethrow the error
				LogWriteError(exception);
				throw;
			}
		}
		#endregion

		#region Private Methods
		/// <summary>
		/// Validates the and store parameters.
		/// </summary>
		/// <param name="databaseInstance">The database instance.</param>
		/// <param name="databaseCommand">The database command.</param>
		/// <param name="databaseCommandType">Type of the database command.</param>
		private void ValidateAndStoreParameters(Database databaseInstance, string databaseCommand, CommandType databaseCommandType)
		{
			// check for a null database instance
			if (databaseInstance == null)
				throw new ArgumentNullException("databaseInstance", InternalResources.ExceptionDatabaseInstanceNull);

			// check for a null command name
			if (String.IsNullOrEmpty(databaseCommand))
				throw new ArgumentNullException("databaseCommand", InternalResources.ExceptionDatabaseCommandNullOrEmpty);

			// store the parameters
			this.database = databaseInstance;
			this.command = databaseCommand;
			this.commandType = databaseCommandType;
		}

		/// <summary>
		/// Builds the command to be invoked.
		/// </summary>
		/// <returns>An <see cref="ICommand"/> object</returns>
		protected override ICommand BuildCommand()
		{
			// check for a custom command
			if (CustomCommand == null)
				return new DataCommand(database, command, commandType, ParameterSet.Parameters);
			else
			{
				DataCommand customDataCommand = (DataCommand)(CustomCommand.Command);
				customDataCommand.Database = database;
				customDataCommand.Command = command;
				customDataCommand.CommandType = commandType;
				customDataCommand.AddParameters(ParameterSet.Parameters);
				return customDataCommand;
			}
		}
		#endregion
	}
}
